home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 40
/
Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso
/
Aminet
/
dev
/
src
/
xad_CAB.lha
/
CAB.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-08-06
|
77KB
|
2,287 lines
/* CAB -- an XAD client for extracting Microsoft Cabinet files
* (C) 2000 Stuart Caie <kyzer@4u.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Credits:
* The CAB format, and MSZIP/LZX formats are described in the documents
* CABFMT.DOC, LZXFMT.DOC and MSZIPFMT.DOC in the CAB SDK, which is
* (C) 1997 Microsoft Corporation.
*
* MSZIP is a modification of the inflate and deflate methods created by
* Phil Katz. LZX was created by Johnathan Forbes and Tomi Poutanen.
*
* The MSZIP part of this client was written for me by Dirk Stöcker, who
* based it on code from InfoZip's free UnZip utility. Dirk also provided
* extensive testing materials, feedback and moral support (oh - and he
* created the XAD system :). I took the fast Huffman table builder from
* David Tritscher's unlzx (no relation :) and adapted to my needs.
*/
/* CAB has pretty much everything - multivolume archives, merged file data,
* and multiple compression formats. 'Folders' store compressed data, and
* may span several cabinets. 'Files' live as data inside a folder when
* uncompressed. EOR checksums are used instead of CRCs. Four compression
* formats are known - NONE, MSZIP, QUANTUM and LZX. NONE is obviously
* uncompressed data. MSZIP is simply PKZIP's deflate/inflate algorithims
* with 'CK' as a signature instead of 'PK'. QUANTUM is a complete unknown,
* and isn't implemented in this client. LZX is a much loved LZH based
* archiver in the Amiga world, the algorithim taken (bought?) by Microsoft
* and tweaked for Intel code.
*/
/* $VER: CAB.c 1.1 (05.08.2000) */
#include "SDI_compiler.h"
#include "ConvertE.c"
#include <exec/types.h>
#include <exec/memory.h>
#include <string.h>
#include <libraries/xadmaster.h>
#include <proto/xadmaster.h>
#ifdef DEBUG
void KPrintF(char *fmt, ...);
#define D(x) { KPrintF x ; }
#else
#define D(x)
#endif
#define CABSTATE struct CABstate *cabstate
#define XADBASE REG(a6, struct xadMasterBase *xadMasterBase)
#define CAB(x) (cabstate->x)
#define ZIP(x) (cabstate->arcstate.zip.x)
#define QTM(x) (cabstate->arcstate.qtm.x)
#define LZX(x) (cabstate->arcstate.lzx.x)
#define CABFILEFOL(fi) ((struct CABfolder *)(fi)->xfi_PrivateInfo)
/* requires UBYTE buf[] */
#define GETLONG(n) EndGetI32(&buf[n])
#define GETWORD(n) EndGetI16(&buf[n])
#define GETBYTE(n) buf[n]
#ifndef XADMASTERFILE
#define CAB_Client FirstClient
#define NEXTCLIENT 0
const UBYTE version[] = "$VER: CAB 1.1 (05.08.2000)";
#endif
#define CAB_VERSION 1
#define CAB_REVISION 0
/* work-doing macros */
/* required: label exit_handler; struct xadArchiveInfo *ai; LONG err; */
#define SKIP(offset) if ((err = xadHookAccess(XADAC_INPUTSEEK, \
(ULONG)(offset), NULL, ai))) goto exit_handler
#define SEEK(offset) SKIP((offset) - ai->xai_InPos)
#define READ(buffer,length) if ((err = xadHookAccess(XADAC_READ, \
(ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define WRITE(buffer,length) if ((err = xadHookAccess(XADAC_WRITE, \
(ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define ERROR(error) { \
D(("CAB: error " #error " line %ld\n", __LINE__)) \
err = XADERR_##error; goto exit_handler; \
}
#define TAINT(reason) { \
ai->xai_Flags |= XADAIF_FILECORRUPT; \
D(("CAB: TAINT - " reason "\n")) \
}
#define ALLOC(t,v,l) \
if (!((v) = (t) xadAllocVec((l),MEMF_CLEAR))) ERROR(NOMEMORY)
#define ALLOCOBJ(t,v,kind,tags) \
if (!((v) = (t) xadAllocObjectA((kind),(tags)))) ERROR(NOMEMORY)
#define FREE(obj) xadFreeObjectA((obj),NULL)
/*--------------------------------------------------------------------------*/
/* our archiver information / state */
/* MSZIP stuff */
#define ZIPWSIZE 0x8000 /* window size--must be a power of two, and at least 32K for zip's deflate method */
#define ZIPLBITS 9 /* bits in base literal/length lookup table */
#define ZIPDBITS 6 /* bits in base distance lookup table */
#define ZIPBMAX 16 /* maximum bit length of any code (16 for explode) */
#define ZIPN_MAX 288 /* maximum number of codes in any set */
struct Ziphuft {
UBYTE e; /* number of extra bits or operation */
UBYTE b; /* number of bits in this code or subcode */
union {
UWORD n; /* literal, length base, or distance base */
struct Ziphuft *t; /* pointer to next level of table */
} v;
};
struct CABZIPstate {
ULONG window_posn; /* current offset within the window */
ULONG bb; /* bit buffer */
ULONG bk; /* bits in bit buffer */
ULONG ll[288+32]; /* literal/length and distance code lengths */
ULONG c[ZIPBMAX+1]; /* bit length count table */
LONG lx[ZIPBMAX+1]; /* memory for l[-1..ZIPBMAX-1] */
struct Ziphuft *u[ZIPBMAX]; /* table stack */
ULONG v[ZIPN_MAX]; /* values in order of bit length */
ULONG x[ZIPBMAX+1]; /* bit offsets, then code stack */
UBYTE *inpos;
};
/* Quantum stuff - not supported yet */
struct CABQTMstate {
UBYTE *window; /* the actual decoding window */
ULONG window_size; /* window size (4Kb through 2Mb) */
ULONG actual_size; /* window size when it was first allocated */
UWORD comp_level; /* level of compression */
};
/* LZX stuff */
/* some constants defined by the LZX specification */
#define LZX_MIN_MATCH (2)
#define LZX_MAX_MATCH (257)
#define LZX_NUM_CHARS (256)
#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */
#define LZX_BLOCKTYPE_VERBATIM (1)
#define LZX_BLOCKTYPE_ALIGNED (2)
#define LZX_BLOCKTYPE_UNCOMPRESSED (3)
#define LZX_PRETREE_NUM_ELEMENTS (20)
#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */
#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */
#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */
/* LZX huffman defines: tweak tablebits as desired */
#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS)
#define LZX_PRETREE_TABLEBITS (6)
#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8)
#define LZX_MAINTREE_TABLEBITS (12)
#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1)
#define LZX_LENGTH_TABLEBITS (12)
#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS)
#define LZX_ALIGNED_TABLEBITS (7)
#define LZX_LENTABLE_SAFETY (64) /* we allow length table decoding overruns */
#define LZX_DECLARE_TABLE(tbl) \
UWORD tbl##_table[(1<<LZX_##tbl##_TABLEBITS) + (LZX_##tbl##_MAXSYMBOLS<<1)];\
UBYTE tbl##_len [LZX_##tbl##_MAXSYMBOLS + LZX_LENTABLE_SAFETY]
struct CABLZXstate {
UBYTE *window; /* the actual decoding window */
ULONG window_size; /* window size (32Kb through 2Mb) */
ULONG actual_size; /* window size when it was first allocated */
ULONG window_posn; /* current offset within the window */
ULONG R0, R1, R2; /* for the LRU offset system */
UWORD main_elements; /* number of main tree elements */
BOOL header_read; /* have we started decoding at all yet? */
UWORD block_type; /* type of this block */
ULONG block_length; /* uncompressed length of this block */
ULONG block_remaining; /* uncompressed bytes still left to decode */
ULONG frames_read; /* the number of CFDATA blocks processed */
LONG intel_filesize; /* magic header value used for transform */
LONG intel_curpos; /* current offset in transform space */
BOOL intel_started; /* have we seen any translatable data yet? */
LZX_DECLARE_TABLE(PRETREE);
LZX_DECLARE_TABLE(MAINTREE);
LZX_DECLARE_TABLE(LENGTH);
LZX_DECLARE_TABLE(ALIGNED);
};
/*--------------------------------------------------------------------------*/
/* CAB structures */
#define CFHEAD_SIGNATURE (('M') | ('S'<<8) | ('C'<<16) | ('F'<<24)) /*intel!*/
#define cfhead_Signature (0x00)
#define cfhead_Reserved1 (0x04)
#define cfhead_CabinetSize (0x08)
#define cfhead_Reserved2 (0x0C)
#define cfhead_FileOffset (0x10)
#define cfhead_Reserved3 (0x14)
#define cfhead_MinorVersion (0x18)
#define cfhead_MajorVersion (0x19)
#define cfhead_NumFolders (0x1A)
#define cfhead_NumFiles (0x1C)
#define cfhead_Flags (0x1E)
#define cfhead_SetID (0x20)
#define cfhead_CabinetIndex (0x22)
#define cfhead_SIZEOF (0x24)
#define cfheadext_HeaderReserved (0x00)
#define cfheadext_FolderReserved (0x02)
#define cfheadext_DataReserved (0x03)
#define cfheadext_SIZEOF (0x04)
/* cfhead_ReservedArea (== HeaderReserved bytes) */
/* cfhead_PrevCabFile (null terminated string) */
/* cfhead_PrevCabName (null terminated string) */
/* cfhead_NextCabFile (null terminated string) */
/* cfhead_NextCabName (null terminated string) */
#define cffold_DataOffset (0x00)
#define cffold_NumBlocks (0x04)
#define cffold_CompType (0x06)
#define cffold_SIZEOF (0x08)
/* cffold_ReservedArea (== FolderReserved bytes) */
#define cffile_UncompressedSize (0x00)
#define cffile_FolderOffset (0x04)
#define cffile_FolderIndex (0x08)
#define cffile_Date (0x0A)
#define cffile_Time (0x0C)
#define cffile_Attribs (0x0E)
#define cffile_SIZEOF (0x10)
/* cffile_FileName (null terminated string) */
#define cfdata_CheckSum (0x00) /* cksum of header/reserved/data */
#define cfdata_CompressedSize (0x04) /* compressed size of block */
#define cfdata_UncompressedSize (0x06) /* uncompressed size of block */
#define cfdata_SIZEOF (0x08)
/* cfdata_ReservedArea (== DataReserved bytes) */
/* flags and values */
#define cffoldCOMPTYPE_MASK (0x000f)
#define cffoldCOMPTYPE_NONE (0x0000)
#define cffoldCOMPTYPE_MSZIP (0x0001)
#define cffoldCOMPTYPE_QUANTUM (0x0002)
#define cffoldCOMPTYPE_LZX (0x0003)
#define cfheadPREV_CABINET (0x0001)
#define cfheadNEXT_CABINET (0x0002)
#define cfheadRESERVE_PRESENT (0x0004)
#define cffileCONTINUED_FROM_PREV (0xFFFD)
#define cffileCONTINUED_TO_NEXT (0xFFFE)
#define cffileCONTINUED_PREV_AND_NEXT (0xFFFF)
#define cffileUTFNAME (0x80)
#define CAB_NAMEMAX 512 /* maximum length of a single path/filename */
/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed
* blocks have zero growth. MSZIP guarantees that it won't grow above
* uncompressed size by more than 12 bytes. LZX guarantees it won't grow
* more than 6144 bytes.
*/
#define CAB_BLOCKMAX (32768)
#define CAB_INPUTMAX (CAB_BLOCKMAX+6144)
/* maximum number of split blocks in any one folder */
#define CAB_SPLITMAX (10)
struct CABfolder {
struct CABfolder *next;
ULONG offsets[CAB_SPLITMAX]; /* offset of first/split data blocks */
UBYTE data_res[CAB_SPLITMAX]; /* bytes reserved in block headers */
ULONG comp_size; /* number of compressed bytes in this part */
UWORD comp_type; /* compression format and window size */
UWORD num_splits; /* number of split blocks */
struct xadFileInfo *contfile; /* is this folder continuable? */
};
struct CABstate {
struct xadMasterBase *xad; /* XAD library base */
struct xadArchiveInfo *ai; /* archive we're extracting from */
struct CABfolder *folders; /* linked list of all folders */
struct CABfolder *current; /* current folder we're extracting from */
ULONG offset; /* uncompressed offset within folder */
UBYTE *outpos; /* (high level) start of data to use up */
UWORD outlen; /* (high level) amount of data to use up */
UBYTE split; /* at which split in current folder? */
/* to speed up the arrival of an error message for trashed files */
struct CABfolder *lastfolder;
ULONG lastoffset;
LONG lasterror;
/* the chosen compression type functions */
LONG (*decompress)(CABSTATE, int, int);
void (*free)(CABSTATE);
union {
struct CABZIPstate zip;
struct CABQTMstate qtm;
struct CABLZXstate lzx;
} arcstate;
UBYTE inbuf[CAB_INPUTMAX], outbuf[CAB_BLOCKMAX];
};
/*--------------------------------------------------------------------------*/
/* MSZIP decompressor */
/* This part was written by Dirk Stöcker, based on the InfoZip deflate code */
/* Tables for deflate from PKZIP's appnote.txt. */
static const UBYTE Zipborder[] = /* Order of the bit length code lengths */
{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
static const UWORD Zipcplens[] = /* Copy lengths for literal codes 257..285 */
{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51,
59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const UWORD Zipcplext[] = /* Extra bits for literal codes 257..285 */
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,
4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
static const UWORD Zipcpdist[] = /* Copy offsets for distance codes 0..29 */
{ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
static const UWORD Zipcpdext[] = /* Extra bits for distance codes */
{ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10,
10, 11, 11, 12, 12, 13, 13};
/* And'ing with Zipmask[n] masks the lower n bits */
static const UWORD Zipmask[17] = {
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#define ZIPNEEDBITS(n) {while(k<(n)){LONG c=*(ZIP(inpos)++);\
b|=((ULONG)c)<<k;k+=8;}}
#define ZIPDUMPBITS(n) {b>>=(n);k-=(n);}
static LONG Ziphuft_free(CABSTATE, struct Ziphuft *t)
{
struct xadMasterBase *xadMasterBase = CAB(xad);
register struct Ziphuft *p, *q;
/* Go through linked list, freeing from the allocated (t[-1]) address. */
p = t;
while (p != (struct Ziphuft *)NULL)
{
q = (--p)->v.t;
xadFreeObjectA(p, 0);
p = q;
}
return 0;
}
static LONG Ziphuft_build(CABSTATE, ULONG *b, ULONG n, ULONG s, UWORD *d, UWORD *e, struct Ziphuft **t, LONG *m)
{
ULONG a; /* counter for codes of length k */
ULONG el; /* length of EOB code (value 256) */
ULONG f; /* i repeats in table every f entries */
LONG g; /* maximum code length */
LONG h; /* table level */
register ULONG i; /* counter, current code */
register ULONG j; /* counter */
register LONG k; /* number of bits in current code */
LONG *l; /* stack of bits per table */
register ULONG *p; /* pointer into ZIP(c)[], ZIP(b)[], or ZIP(v)[] */
register struct Ziphuft *q; /* points to current table */
struct Ziphuft r; /* table entry for structure assignment */
register LONG w; /* bits before this table == (l * h) */
ULONG *xp; /* pointer into x */
LONG y; /* number of dummy codes added */
ULONG z; /* number of entries in current table */
struct xadMasterBase *xadMasterBase = CAB(xad);
l = ZIP(lx)+1;
/* Generate counts for each bit length */
el = n > 256 ? b[256] : ZIPBMAX; /* set length of EOB code, if any */
for(i = 0; i < ZIPBMAX+1; ++i)
ZIP(c)[i] = 0;
p = b; i = n;
do
{
ZIP(c)[*p]++; p++; /* assume all entries <= ZIPBMAX */
} while (--i);
if (ZIP(c)[0] == n) /* null input--all zero length codes */
{
*t = (struct Ziphuft *)NULL;
*m = 0;
return 0;
}
/* Find minimum and maximum length, bound *m by those */
for (j = 1; j <= ZIPBMAX; j++)
if (ZIP(c)[j])
break;
k = j; /* minimum code length */
if ((ULONG)*m < j)
*m = j;
for (i = ZIPBMAX; i; i--)
if (ZIP(c)[i])
break;
g = i; /* maximum code length */
if ((ULONG)*m > i)
*m = i;
/* Adjust last length count to fill out codes, if needed */
for (y = 1 << j; j < i; j++, y <<= 1)
if ((y -= ZIP(c)[j]) < 0)
return 2; /* bad input: more codes than bits */
if ((y -= ZIP(c)[i]) < 0)
return 2;
ZIP(c)[i] += y;
/* Generate starting offsets LONGo the value table for each length */
ZIP(x)[1] = j = 0;
p = ZIP(c) + 1; xp = ZIP(x) + 2;
while (--i)
{ /* note that i == g from above */
*xp++ = (j += *p++);
}
/* Make a table of values in order of bit lengths */
p = b; i = 0;
do{
if ((j = *p++) != 0)
ZIP(v)[ZIP(x)[j]++] = i;
} while (++i < n);
/* Generate the Huffman codes and for each, make the table entries */
ZIP(x)[0] = i = 0; /* first Huffman code is zero */
p = ZIP(v); /* grab values in bit order */
h = -1; /* no tables yet--level -1 */
w = l[-1] = 0; /* no bits decoded yet */
ZIP(u)[0] = (struct Ziphuft *)NULL; /* just to keep compilers happy */
q = (struct Ziphuft *)NULL; /* ditto */
z = 0; /* ditto */
/* go through the bit lengths (k already is bits in shortest code) */
for (; k <= g; k++)
{
a = ZIP(c)[k];
while (a--)
{
/* here i is the Huffman code of length k bits for value *p */
/* make tables up to required level */
while (k > w + l[h])
{
w += l[h++]; /* add bits already decoded */
/* compute minimum size table less than or equal to *m bits */
z = (z = g - w) > (ULONG)*m ? *m : z; /* upper limit */
if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
{ /* too few codes for k-w bit table */
f -= a + 1; /* deduct codes from patterns left */
xp = ZIP(c) + k;
while (++j < z) /* try smaller tables up to z bits */
{
if ((f <<= 1) <= *++xp)
break; /* enough codes to use up j bits */
f -= *xp; /* else deduct codes from patterns */
}
}
if ((ULONG)w + j > el && (ULONG)w < el)
j = el - w; /* make EOB code end at table */
z = 1 << j; /* table entries for j-bit table */
l[h] = j; /* set table size in stack */
/* allocate and link in new table */
if (!(q = (struct Ziphuft *) xadAllocVec((z + 1)*sizeof(struct Ziphuft), MEMF_PUBLIC)))
{
if(h)
Ziphuft_free(cabstate, ZIP(u)[0]);
return 3; /* not enough memory */
}
*t = q + 1; /* link to list for Ziphuft_free() */
*(t = &(q->v.t)) = (struct Ziphuft *)NULL;
ZIP(u)[h] = ++q; /* table starts after link */
/* connect to last table, if there is one */
if (h)
{
ZIP(x)[h] = i; /* save pattern for backing up */
r.b = (UBYTE)l[h-1]; /* bits to dump before this table */
r.e = (UBYTE)(16 + j); /* bits in this table */
r.v.t = q; /* pointer to this table */
j = (i & ((1 << w) - 1)) >> (w - l[h-1]);
ZIP(u)[h-1][j] = r; /* connect to last table */
}
}
/* set up table entry in r */
r.b = (UBYTE)(k - w);
if (p >= ZIP(v) + n)
r.e = 99; /* out of values--invalid code */
else if (*p < s)
{
r.e = (UBYTE)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */
r.v.n = *p++; /* simple code is just the value */
}
else
{
r.e = (UBYTE)e[*p - s]; /* non-simple--look up in lists */
r.v.n = d[*p++ - s];
}
/* fill code-like entries with r */
f = 1 << (k - w);
for (j = i >> w; j < z; j += f)
q[j] = r;
/* backwards increment the k-bit code i */
for (j = 1 << (k - 1); i & j; j >>= 1)
i ^= j;
i ^= j;
/* backup over finished tables */
while ((i & ((1 << w) - 1)) != ZIP(x)[h])
w -= l[--h]; /* don't need to update q */
}
}
/* return actual size of base table */
*m = l[0];
/* Return true (1) if we were given an incomplete table */
return y != 0 && g != 1;
}
static LONG Zipinflate_codes(CABSTATE, struct Ziphuft *tl, struct Ziphuft *td, LONG bl, LONG bd)
{
register ULONG e; /* table entry flag/number of extra bits */
ULONG n, d; /* length and index for copy */
ULONG w; /* current window position */
struct Ziphuft *t; /* pointer to table entry */
ULONG ml, md; /* masks for bl and bd bits */
register ULONG b; /* bit buffer */
register ULONG k; /* number of bits in bit buffer */
/* make local copies of globals */
b = ZIP(bb); /* initialize bit buffer */
k = ZIP(bk);
w = ZIP(window_posn); /* initialize window position */
/* inflate the coded data */
ml = Zipmask[bl]; /* precompute masks for speed */
md = Zipmask[bd];
for(;;)
{
ZIPNEEDBITS((ULONG)bl)
if((e = (t = tl + ((ULONG)b & ml))->e) > 16)
do
{
if (e == 99)
return 1;
ZIPDUMPBITS(t->b)
e -= 16;
ZIPNEEDBITS(e)
} while ((e = (t = t->v.t + ((ULONG)b & Zipmask[e]))->e) > 16);
ZIPDUMPBITS(t->b)
if (e == 16) /* then it's a literal */
CAB(outbuf)[w++] = (UBYTE)t->v.n;
else /* it's an EOB or a length */
{
/* exit if end of block */
if(e == 15)
break;
/* get length of block to copy */
ZIPNEEDBITS(e)
n = t->v.n + ((ULONG)b & Zipmask[e]);
ZIPDUMPBITS(e);
/* decode distance of block to copy */
ZIPNEEDBITS((ULONG)bd)
if ((e = (t = td + ((ULONG)b & md))->e) > 16)
do {
if (e == 99)
return 1;
ZIPDUMPBITS(t->b)
e -= 16;
ZIPNEEDBITS(e)
} while ((e = (t = t->v.t + ((ULONG)b & Zipmask[e]))->e) > 16);
ZIPDUMPBITS(t->b)
ZIPNEEDBITS(e)
d = w - t->v.n - ((ULONG)b & Zipmask[e]);
ZIPDUMPBITS(e)
do
{
n -= (e = (e = ZIPWSIZE - ((d &= ZIPWSIZE-1) > w ? d : w)) > n ? n : e);
do
{
CAB(outbuf)[w++] = CAB(outbuf)[d++];
} while (--e);
} while (n);
}
}
/* restore the globals from the locals */
ZIP(window_posn) = w; /* restore global window pointer */
ZIP(bb) = b; /* restore global bit buffer */
ZIP(bk) = k;
/* done */
return 0;
}
static LONG Zipinflate_stored(CABSTATE) /* "decompress" an inflated type 0 (stored) block. */
{
ULONG n; /* number of bytes in block */
ULONG w; /* current window position */
register ULONG b; /* bit buffer */
register ULONG k; /* number of bits in bit buffer */
/* make local copies of globals */
b = ZIP(bb); /* initialize bit buffer */
k = ZIP(bk);
w = ZIP(window_posn); /* initialize window position */
/* go to byte boundary */
n = k & 7;
ZIPDUMPBITS(n);
/* get the length and its complement */
ZIPNEEDBITS(16)
n = ((ULONG)b & 0xffff);
ZIPDUMPBITS(16)
ZIPNEEDBITS(16)
if (n != (ULONG)((~b) & 0xffff))
return 1; /* error in compressed data */
ZIPDUMPBITS(16)
/* read and output the compressed data */
while(n--)
{
ZIPNEEDBITS(8)
CAB(outbuf)[w++] = (UBYTE)b;
ZIPDUMPBITS(8)
}
/* restore the globals from the locals */
ZIP(window_posn) = w; /* restore global window pointer */
ZIP(bb) = b; /* restore global bit buffer */
ZIP(bk) = k;
return 0;
}
static LONG Zipinflate_fixed(CABSTATE)
{
struct Ziphuft *fixed_tl;
struct Ziphuft *fixed_td;
LONG fixed_bl, fixed_bd;
LONG i; /* temporary variable */
ULONG *l;
l = ZIP(ll);
/* literal table */
for(i = 0; i < 144; i++)
l[i] = 8;
for(; i < 256; i++)
l[i] = 9;
for(; i < 280; i++)
l[i] = 7;
for(; i < 288; i++) /* make a complete, but wrong code set */
l[i] = 8;
fixed_bl = 7;
if((i = Ziphuft_build(cabstate, l, 288, 257, (UWORD *) Zipcplens, (UWORD *) Zipcplext, &fixed_tl, &fixed_bl)))
return i;
/* distance table */
for(i = 0; i < 30; i++) /* make an incomplete code set */
l[i] = 5;
fixed_bd = 5;
if((i = Ziphuft_build(cabstate, l, 30, 0, (UWORD *) Zipcpdist, (UWORD *) Zipcpdext, &fixed_td, &fixed_bd)) > 1)
{
Ziphuft_free(cabstate, fixed_tl);
return i;
}
/* decompress until an end-of-block code */
i = Zipinflate_codes(cabstate, fixed_tl, fixed_td, fixed_bl, fixed_bd);
Ziphuft_free(cabstate, fixed_td);
Ziphuft_free(cabstate, fixed_tl);
return i;
}
static LONG Zipinflate_dynamic(CABSTATE) /* decompress an inflated type 2 (dynamic Huffman codes) block. */
{
LONG i; /* temporary variables */
ULONG j;
ULONG *ll;
ULONG l; /* last length */
ULONG m; /* mask for bit lengths table */
ULONG n; /* number of lengths to get */
struct Ziphuft *tl; /* literal/length code table */
struct Ziphuft *td; /* distance code table */
LONG bl; /* lookup bits for tl */
LONG bd; /* lookup bits for td */
ULONG nb; /* number of bit length codes */
ULONG nl; /* number of literal/length codes */
ULONG nd; /* number of distance codes */
register ULONG b; /* bit buffer */
register ULONG k; /* number of bits in bit buffer */
/* make local bit buffer */
b = ZIP(bb);
k = ZIP(bk);
ll = ZIP(ll);
/* read in table lengths */
ZIPNEEDBITS(5)
nl = 257 + ((ULONG)b & 0x1f); /* number of literal/length codes */
ZIPDUMPBITS(5)
ZIPNEEDBITS(5)
nd = 1 + ((ULONG)b & 0x1f); /* number of distance codes */
ZIPDUMPBITS(5)
ZIPNEEDBITS(4)
nb = 4 + ((ULONG)b & 0xf); /* number of bit length codes */
ZIPDUMPBITS(4)
if(nl > 288 || nd > 32)
return 1; /* bad lengths */
/* read in bit-length-code lengths */
for(j = 0; j < nb; j++)
{
ZIPNEEDBITS(3)
ll[Zipborder[j]] = (ULONG)b & 7;
ZIPDUMPBITS(3)
}
for(; j < 19; j++)
ll[Zipborder[j]] = 0;
/* build decoding table for trees--single level, 7 bit lookup */
bl = 7;
if((i = Ziphuft_build(cabstate, ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
{
if(i == 1)
Ziphuft_free(cabstate, tl);
return i; /* incomplete code set */
}
/* read in literal and distance code lengths */
n = nl + nd;
m = Zipmask[bl];
i = l = 0;
while((ULONG)i < n)
{
ZIPNEEDBITS((ULONG)bl)
j = (td = tl + ((ULONG)b & m))->b;
ZIPDUMPBITS(j)
j = td->v.n;
if (j < 16) /* length of code in bits (0..15) */
ll[i++] = l = j; /* save last length in l */
else if (j == 16) /* repeat last length 3 to 6 times */
{
ZIPNEEDBITS(2)
j = 3 + ((ULONG)b & 3);
ZIPDUMPBITS(2)
if((ULONG)i + j > n)
return 1;
while (j--)
ll[i++] = l;
}
else if (j == 17) /* 3 to 10 zero length codes */
{
ZIPNEEDBITS(3)
j = 3 + ((ULONG)b & 7);
ZIPDUMPBITS(3)
if ((ULONG)i + j > n)
return 1;
while (j--)
ll[i++] = 0;
l = 0;
}
else /* j == 18: 11 to 138 zero length codes */
{
ZIPNEEDBITS(7)
j = 11 + ((ULONG)b & 0x7f);
ZIPDUMPBITS(7)
if ((ULONG)i + j > n)
return 1;
while (j--)
ll[i++] = 0;
l = 0;
}
}
/* free decoding table for trees */
Ziphuft_free(cabstate, tl);
/* restore the global bit buffer */
ZIP(bb) = b;
ZIP(bk) = k;
/* build the decoding tables for literal/length and distance codes */
bl = ZIPLBITS;
if((i = Ziphuft_build(cabstate, ll, nl, 257, (UWORD *) Zipcplens, (UWORD *) Zipcplext, &tl, &bl)) != 0)
{
if(i == 1)
Ziphuft_free(cabstate, tl);
return i; /* incomplete code set */
}
bd = ZIPDBITS;
Ziphuft_build(cabstate, ll + nl, nd, 0, (UWORD *) Zipcpdist, (UWORD *) Zipcpdext, &td, &bd);
/* decompress until an end-of-block code */
if(Zipinflate_codes(cabstate, tl, td, bl, bd))
return 1;
/* free the decoding tables, return */
Ziphuft_free(cabstate, tl);
Ziphuft_free(cabstate, td);
return 0;
}
static LONG Zipinflate_block(CABSTATE, LONG *e) /* e == last block flag */
{ /* decompress an inflated block */
ULONG t; /* block type */
register ULONG b; /* bit buffer */
register ULONG k; /* number of bits in bit buffer */
/* make local bit buffer */
b = ZIP(bb);
k = ZIP(bk);
/* read in last block bit */
ZIPNEEDBITS(1)
*e = (LONG)b & 1;
ZIPDUMPBITS(1)
/* read in block type */
ZIPNEEDBITS(2)
t = (ULONG)b & 3;
ZIPDUMPBITS(2)
/* restore the global bit buffer */
ZIP(bb) = b;
ZIP(bk) = k;
D(("ZIP: blocktype=%ld, last block=%ld\n", t, *e))
/* inflate that block type */
if(t == 2)
return Zipinflate_dynamic(cabstate);
if(t == 0)
return Zipinflate_stored(cabstate);
if(t == 1)
return Zipinflate_fixed(cabstate);
/* bad block type */
return 2;
}
LONG CAB_ZIPdecompress(CABSTATE, int inlen, int outlen)
{
LONG e; /* last block flag */
D(("ZIP: outlen %ld\n", outlen))
ZIP(inpos) = CAB(inbuf);
ZIP(bb) = ZIP(bk) = ZIP(window_posn) = 0;
if(outlen > ZIPWSIZE)
return XADERR_DATAFORMAT;
/* CK = Chris Kirmse, official Microsoft purloiner */
if(ZIP(inpos)[0] != 0x43 || ZIP(inpos)[1] != 0x4B)
return XADERR_ILLEGALDATA;
ZIP(inpos) += 2;
do
{
if(Zipinflate_block(cabstate, &e))
return XADERR_ILLEGALDATA;
} while(!e);
/* return success */
return XADERR_OK;
}
/*--------------------------------------------------------------------------*/
/* Quantum decompressor */
LONG CAB_QTMinit(CABSTATE, int level, int window) {
struct xadMasterBase *xadMasterBase = CAB(xad);
ULONG wndsize = 1 << window;
/* Quantum supports window sizes of 2^10 (4Kb) through 2^21 (2Mb) */
/* if a previously allocated window is big enough, keep it */
if (window < 10 || window > 21) return XADERR_DATAFORMAT;
if (QTM(actual_size) < wndsize) {
if (QTM(window)) FREE(QTM(window));
QTM(window) = NULL;
}
if (!QTM(window)) {
/* not using ALLOC() macro because we don't need to clear the window */
if (!(QTM(window) = xadAllocVec(wndsize, 0))) return XADERR_NOMEMORY;
QTM(actual_size) = wndsize;
}
QTM(window_size) = wndsize;
QTM(comp_level) = level;
return XADERR_OK;
}
void CAB_QTMfree(CABSTATE) {
struct xadMasterBase *xadMasterBase = CAB(xad);
if (QTM(window)) FREE(QTM(window));
QTM(window) = NULL;
}
LONG CAB_QTMdecompress(CABSTATE, int inlen, int outlen) {
return XADERR_DATAFORMAT;
}
/*--------------------------------------------------------------------------*/
/* LZX decompressor */
/* Microsoft's LZX document and their implementation of the
* com.ms.util.cab Java package do not concur.
*
* Correlation between window size and number of position slots: In the
* LZX document, 1MB window = 40 slots, 2MB window = 42 slots. In the
* implementation, 1MB = 42 slots, 2MB = 50 slots. (The actual calculation
* is 'find the first slot whose position base is equal to or more than the
* required window size'). This would explain why other tables in the
* document refer to 50 slots rather than 42.
*
* The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
* is not defined in the specification, although it could be derived from
* the section on encoding match lengths.
*
* The LZX document does not state the uncompressed block has an
* uncompressed length. Where does this length field come from, so we can
* know how large the block is? The implementation suggests that it's in
* the 24 bits proceeding the 3 blocktype bits, before the alignment
* padding.
*
* The LZX document states that aligned offset blocks have their aligned
* offset huffman tree AFTER the main and length tree. The implementation
* suggests that the aligned offset tree is BEFORE the main and length trees.
*
* The LZX document decoding algorithim states that, in an aligned offset
* block, if an extra_bits value is 1, 2 or 3, then that number of bits
* should be read and the result added to the match offset. This is correct
* for 1 and 2, but not 3 bits, where only an aligned symbol should be read.
*/
/* LZX uses what it calls 'position slots' to represent match offsets.
* What this means is that a small 'position slot' number and a small
* offset from that slot are encoded instead of one large offset for
* every match.
* - position_base is an index to the position slot bases
* - extra_bits states how many bits of offset-from-base data is needed.
*/
static ULONG position_base[51];
static UBYTE extra_bits[51];
LONG CAB_LZXinit(CABSTATE, int window) {
struct xadMasterBase *xadMasterBase = CAB(xad);
int wndsize = 1 << window;
int i, j, posn_slots;
D(("LZX: init wndsize=%ld\n", window))
/* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
/* if a previously allocated window is big enough, keep it */
if (window < 15 || window > 21) return XADERR_DATAFORMAT;
if (LZX(actual_size) < wndsize) {
if (LZX(window)) FREE(LZX(window));
LZX(window) = NULL;
}
if (!LZX(window)) {
/* not using ALLOC() macro because we don't need to clear the window */
if (!(LZX(window) = xadAllocVec(wndsize, 0))) return XADERR_NOMEMORY;
LZX(actual_size) = wndsize;
}
LZX(window_size) = wndsize;
/* initialise static tables */
for (i=0, j=0; i <= 50; i += 2) {
extra_bits[i] = extra_bits[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */
if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */
}
for (i=0, j=0; i <= 50; i++) {
position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */
j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */
}
/* calculate required position slots */
if (window == 20) posn_slots = 42;
else if (window == 21) posn_slots = 50;
else posn_slots = window << 1;
/*posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
LZX(R0) = LZX(R1) = LZX(R2) = 1;
LZX(main_elements) = LZX_NUM_CHARS + (posn_slots << 3);
LZX(header_read) = 0;
LZX(frames_read) = 0;
LZX(block_remaining) = 0;
LZX(block_type) = LZX_BLOCKTYPE_INVALID;
LZX(intel_curpos) = 0;
LZX(intel_started) = 0;
LZX(window_posn) = 0;
/* initialise tables to 0 (because deltas will be applied to them) */
for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LZX(LENGTH_len)[i] = 0;
return XADERR_OK;
}
void CAB_LZXfree(CABSTATE) {
struct xadMasterBase *xadMasterBase = CAB(xad);
if (LZX(window)) FREE(LZX(window));
LZX(window) = NULL;
}
/* Bitstream reading macros:
*
* INIT_BITSTREAM should be used first to set up the system
* READ_BITS(var,n) takes N bits from the buffer and puts them in var
*
* ENSURE_BITS(n) ensures there are at least N bits in the bit buffer
* PEEK_BITS(n) extracts (without removing) N bits from the bit buffer
* REMOVE_BITS(n) removes N bits from the bit buffer
*
* These bit access routines work by using the area beyond the MSB and the
* LSB as a free source of zeroes. This avoids having to mask any bits.
* So we have to know the bit width of the bitbuffer variable. This is
* sizeof(ULONG) * 8, also defined as ULONG_BITS
*/
/* number of bits in ULONG. Note: This must be at multiple of 16, and at
* least 32 for the bitbuffer code to work (ie, it must be able to ensure
* up to 17 bits - that's adding 16 bits when there's one bit left, or
* adding 32 bits when there are no bits left. The code should work fine
* for machines where ULONG >= 32 bits.
*/
#define ULONG_BITS (sizeof(ULONG)<<3)
#define INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0)
#define ENSURE_BITS(n) \
while (bitsleft < (n)) { \
bitbuf |= ((inpos[1]<<8)|inpos[0]) << (ULONG_BITS-16 - bitsleft); \
bitsleft += 16; inpos+=2; \
}
#define PEEK_BITS(n) (bitbuf >> (ULONG_BITS - (n)))
#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n)))
#define READ_BITS(v,n) do { \
ENSURE_BITS(n); \
(v) = PEEK_BITS(n); \
REMOVE_BITS(n); \
/*D(("getbits(%ld)=%ld\n",n,(v)))*/ \
} while (0)
/* Huffman macros */
#define TABLEBITS(tbl) (LZX_##tbl##_TABLEBITS)
#define MAXSYMBOLS(tbl) (LZX_##tbl##_MAXSYMBOLS)
#define SYMTABLE(tbl) (LZX(tbl##_table))
#define LENTABLE(tbl) (LZX(tbl##_len))
/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths.
* In reality, it just calls make_decode_table() with the appropriate
* values - they're all fixed by some #defines anyway, so there's no point
* writing each call out in full by hand.
*/
#define BUILD_TABLE(tbl) \
if (make_decode_table( \
MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl) \
)) { D(("LZX: table failure\n")) return XADERR_ILLEGALDATA; }
/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
* bitstream using the stated table and puts it in var.
*/
#define READ_HUFFSYM(tbl,var) do { \
ENSURE_BITS(16); \
hufftbl = SYMTABLE(tbl); \
if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) { \
j = 1 << (ULONG_BITS - TABLEBITS(tbl)); \
do { \
j >>= 1; i <<= 1; i |= (bitbuf & j) ? 1 : 0; \
if (!j) return XADERR_ILLEGALDATA; \
} while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl)); \
} \
j = LENTABLE(tbl)[(var) = i]; \
REMOVE_BITS(j); \
} while (0)
/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
* first to last in the given table. The code lengths are stored in their
* own special LZX way. Note that we pass in an lzx_bits structure to
* get the bitstream state between the function and the caller - this has
* to be initialised before using READ_LENGTHS, and retrieved again before
* the bit macros are next used.
*/
#define READ_LENGTHS(tbl,first,last) \
if (lzx_read_lens(cabstate, LENTABLE(tbl), (first), (last), &lb)) \
return XADERR_ILLEGALDATA;
struct lzx_bits {
ULONG bb;
int bl;
UBYTE *ip;
};
/* make_decode_table(nsyms, nbits, length[], table[])
*
* This function was coded by David Tritscher. It builds a fast huffman
* decoding table out of just a canonical huffman code lengths table.
*
* nsyms = total number of symbols in this huffman tree.
* nbits = any symbols with a code length of nbits or less can be decoded
* in one lookup of the table.
* length = A table to get code lengths from [0 to syms-1]
* table = The table to fill up with decoded symbols and pointers.
*
* Returns 0 for OK or 1 for error
*/
int make_decode_table(int nsyms, int nbits, UBYTE *length, UWORD *table) {
register UWORD sym;
register ULONG leaf;
register UBYTE bit_num = 1;
ULONG fill;
ULONG pos = 0; /* the current position in the decode table */
ULONG table_mask = 1 << nbits;
ULONG bit_mask = table_mask >> 1; /* don't do 0 length codes */
ULONG next_symbol = bit_mask; /* base of allocation for long codes */
/* fill entries for codes short enough for a direct mapping */
while (bit_num <= nbits) {
for (sym = 0; sym < nsyms; sym++) {
if (length[sym] == bit_num) {
leaf = pos;
if((pos += bit_mask) > table_mask) return 1; /* table overrun */
/* fill all possible lookups of this symbol with the symbol itself */
fill = bit_mask;
while (fill-- > 0) table[leaf++] = sym;
}
}
bit_mask >>= 1;
bit_num++;
}
/* if there are any codes longer than nbits */
if (pos != table_mask) {
/* clear the remainder of the table */
for (sym = pos; sym < table_mask; sym++) table[sym] = 0;
/* give ourselves room for codes to grow by up to 16 more bits */
pos <<= 16;
table_mask <<= 16;
bit_mask = 1 << 15;
while (bit_num <= 16) {
for (sym = 0; sym < nsyms; sym++) {
if (length[sym] == bit_num) {
leaf = pos >> 16;
for (fill = 0; fill < bit_num - nbits; fill++) {
/* if this path hasn't been taken yet, 'allocate' two entries */
if (table[leaf] == 0) {
table[(next_symbol << 1)] = 0;
table[(next_symbol << 1) + 1] = 0;
table[leaf] = next_symbol++;
}
/* follow the path and select either left or right for next bit */
leaf = table[leaf] << 1;
if ((pos >> (15-fill)) & 1) leaf++;
}
table[leaf] = sym;
if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
}
}
bit_mask >>= 1;
bit_num++;
}
}
/* full table? */
if (pos == table_mask) return 0;
/* either erroneous table, or all elements are 0 - let's find out. */
for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1;
return 0;
}
int lzx_read_lens(CABSTATE, UBYTE *lens, int f, int l, struct lzx_bits *lb) {
ULONG i,j, x,y;
int z;
register ULONG bitbuf = lb->bb;
register int bitsleft = lb->bl;
UBYTE *inpos = lb->ip;
UWORD *hufftbl;
for (x = 0; x < 20; x++) {
READ_BITS(y, 4);
LENTABLE(PRETREE)[x] = y;
}
BUILD_TABLE(PRETREE);
for (x = f; x < l; ) {
READ_HUFFSYM(PRETREE, z);
if (z == 17) {
READ_BITS(y, 4); y += 4;
while (y--) lens[x++] = 0;
}
else if (z == 18) {
READ_BITS(y, 5); y += 20;
while (y--) lens[x++] = 0;
}
else if (z == 19) {
READ_BITS(y, 1); y += 4;
READ_HUFFSYM(PRETREE, z);
z = lens[x] - z; if (z < 0) z += 17;
while (y--) lens[x++] = z;
}
else {
z = lens[x] - z; if (z < 0) z += 17;
lens[x++] = z;
}
}
lb->bb = bitbuf;
lb->bl = bitsleft;
lb->ip = inpos;
/*for (x = f; x < l; x++) D(("length[%ld]=%ld\n", x, lens[x]))*/
return 0;
}
LONG CAB_LZXdecompress(CABSTATE, int inlen, int outlen) {
struct xadMasterBase *xadMasterBase = CAB(xad);
UBYTE *inpos = CAB(inbuf);
UBYTE *endinp = inpos + inlen;
UBYTE *window = LZX(window);
UBYTE *runsrc, *rundest;
UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */
ULONG window_posn = LZX(window_posn);
ULONG window_size = LZX(window_size);
ULONG R0 = LZX(R0);
ULONG R1 = LZX(R1);
ULONG R2 = LZX(R2);
register ULONG bitbuf;
register int bitsleft;
ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
struct lzx_bits lb; /* used in READ_LENGTHS macro */
int togo = outlen, this_run, main_element, aligned_bits;
int match_length, length_footer, extra, verbatim_bits;
INIT_BITSTREAM;
/* read header if necessary */
if (!LZX(header_read)) {
i = j = 0;
READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); }
LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */
LZX(header_read) = 1;
}
/* main decoding loop */
while (togo > 0) {
D(("LZX: top of loop, %ld togo\n", togo))
/* last block finished, new block expected */
if (LZX(block_remaining) == 0) {
if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) {
if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */
INIT_BITSTREAM;
D(("LZX: aligning after previous uncompressed block\n"))
}
READ_BITS(LZX(block_type), 3);
READ_BITS(i, 16);
READ_BITS(j, 8);
LZX(block_remaining) = LZX(block_length) = (i << 8) | j;
D(("LZX: new %ld block len=%ld\n", LZX(block_type), LZX(block_length)))
switch (LZX(block_type)) {
case LZX_BLOCKTYPE_ALIGNED:
for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; }
BUILD_TABLE(ALIGNED);
/* rest of aligned header is same as verbatim */
case LZX_BLOCKTYPE_VERBATIM:
/* set up a pass-in structure with bitstream state for READ_LENGTHS */
lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos;
READ_LENGTHS(MAINTREE, 0, 256);
READ_LENGTHS(MAINTREE, 256, LZX(main_elements));
BUILD_TABLE(MAINTREE);
if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1;
READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE(LENGTH);
/* retrieve the bitstream state from the readlens structure */
bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip;
break;
case LZX_BLOCKTYPE_UNCOMPRESSED:
LZX(intel_started) = 1; /* because we can't assume otherwise */
ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */
if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */
LZX(R0)=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
LZX(R1)=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
LZX(R2)=inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
D(("LZX: uncomp header; Rx=%ld/%ld/%ld\n",LZX(R0),LZX(R1),LZX(R2)))
break;
default:
return XADERR_ILLEGALDATA;
}
D(("LZX: block header read OK\n"))
}
if (inpos > endinp) return XADERR_ILLEGALDATA;
while ((this_run = LZX(block_remaining)) > 0 && togo > 0) {
D(("LZX: block remaining = %ld, togo = %ld\n", this_run, togo))
if (this_run > togo) this_run = togo;
togo -= this_run;
LZX(block_remaining) -= this_run;
/* apply 2^x-1 mask */
window_posn &= window_size - 1;
/* runs can't straddle the window wraparound */
if ((window_posn + this_run) > window_size)
return XADERR_DATAFORMAT;
switch (LZX(block_type)) {
case LZX_BLOCKTYPE_VERBATIM:
while (this_run > 0) {
READ_HUFFSYM(MAINTREE, main_element);
/*D(("%ld\n",main_element))*/
if (main_element < LZX_NUM_CHARS) {
/* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn++] = main_element;
this_run--;
}
else {
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
main_element -= LZX_NUM_CHARS;
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
READ_HUFFSYM(LENGTH, length_footer);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
match_offset = main_element >> 3;
if (match_offset > 2) {
/* not repeated offset */
if (match_offset != 3) {
extra = extra_bits[match_offset];
READ_BITS(verbatim_bits, extra);
match_offset = position_base[match_offset] - 2 + verbatim_bits;
}
else {
match_offset = 1;
}
/* update repeated offset LRU queue */
R2 = R1; R1 = R0; R0 = match_offset;
}
else if (match_offset == 0) {
match_offset = R0;
}
else if (match_offset == 1) {
match_offset = R1;
R1 = R0; R0 = match_offset;
}
else /* match_offset == 2 */ {
match_offset = R2;
R2 = R0; R0 = match_offset;
}
/*D(("%ld,%ld\n",match_length,match_offset))*/
rundest = window + window_posn;
runsrc = rundest - match_offset;
window_posn += match_length;
this_run -= match_length;
/* copy any wrapped around source data */
while ((runsrc < window) && (match_length-- > 0)) {
*rundest++ = *(runsrc + window_size); runsrc++;
}
/* copy match data - no worries about destination wraps */
while (match_length-- > 0) *rundest++ = *runsrc++;
}
}
break;
case LZX_BLOCKTYPE_ALIGNED:
while (this_run > 0) {
READ_HUFFSYM(MAINTREE, main_element);
/*D(("%ld\n",main_element))*/
if (main_element < LZX_NUM_CHARS) {
/* literal: 0 to LZX_NUM_CHARS-1 */
window[window_posn++] = main_element;
this_run--;
}
else {
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
main_element -= LZX_NUM_CHARS;
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
READ_HUFFSYM(LENGTH, length_footer);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
match_offset = main_element >> 3;
if (match_offset > 2) {
/* not repeated offset */
extra = extra_bits[match_offset];
match_offset = position_base[match_offset] - 2;
if (extra > 3) {
/* verbatim and aligned bits */
extra -= 3;
READ_BITS(verbatim_bits, extra);
match_offset += (verbatim_bits << 3);
READ_HUFFSYM(ALIGNED, aligned_bits);
match_offset += aligned_bits;
}
else if (extra == 3) {
/* aligned bits only */
READ_HUFFSYM(ALIGNED, aligned_bits);
match_offset += aligned_bits;
}
else if (extra > 0) { /* extra==1, extra==2 */
/* verbatim bits only */
READ_BITS(verbatim_bits, extra);
match_offset += verbatim_bits;
}
else /* extra == 0 */ {
/* ??? */
match_offset = 1;
}
/* update repeated offset LRU queue */
R2 = R1; R1 = R0; R0 = match_offset;
}
else if (match_offset == 0) {
match_offset = R0;
}
else if (match_offset == 1) {
match_offset = R1;
R1 = R0; R0 = match_offset;
}
else /* match_offset == 2 */ {
match_offset = R2;
R2 = R0; R0 = match_offset;
}
/*D(("%ld,%ld\n",match_length,match_offset))*/
rundest = window + window_posn;
runsrc = rundest - match_offset;
window_posn += match_length;
this_run -= match_length;
/* copy any wrapped around source data */
while ((runsrc < window) && (match_length-- > 0)) {
*rundest++ = *(runsrc + window_size); runsrc++;
}
/* copy match data - no worries about destination wraps */
while (match_length-- > 0) *rundest++ = *runsrc++;
}
}
break;
case LZX_BLOCKTYPE_UNCOMPRESSED:
if ((inpos + this_run) > endinp) return XADERR_ILLEGALDATA;
xadCopyMem(inpos, window + window_posn, this_run);
inpos += this_run; window_posn += this_run;
break;
default:
return XADERR_ILLEGALDATA; /* might as well */
}
}
}
if (togo != 0) return XADERR_ILLEGALDATA;
xadCopyMem(window + ((window_posn == 0) ? window_size : window_posn) -
outlen, CAB(outbuf), outlen);
LZX(window_posn) = window_posn;
LZX(R0) = R0;
LZX(R1) = R1;
LZX(R2) = R2;
/* intel decoding */
if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) {
if (outlen <= 6 || !LZX(intel_started)) {
LZX(intel_curpos) += outlen;
}
else {
UBYTE *data = CAB(outbuf);
UBYTE *dataend = data + outlen - 6;
LONG curpos = LZX(intel_curpos);
LONG filesize = LZX(intel_filesize);
LONG abs_off, rel_off;
LZX(intel_curpos) = curpos + outlen;
while (data < dataend) {
if (*data++ != 0xE8) { curpos++; continue; }
abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
if ((abs_off >= -curpos) && (abs_off < filesize)) {
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
data[0] = (UBYTE) rel_off;
data[1] = (UBYTE) (rel_off >> 8);
data[2] = (UBYTE) (rel_off >> 16);
data[3] = (UBYTE) (rel_off >> 24);
/*D(("LZX: E8 abs=%08lx rel=%08lx\n",abs_off,rel_off))*/
}
data += 4;
curpos += 5;
}
}
}
return XADERR_OK;
}
/*--------------------------------------------------------------------------*/
/* CAB RecogData/GetInfo code */
/* UTF translates two-byte unicode characters into 1, 2 or 3 bytes.
* %000000000xxxxxxx (0x0000 - 0x007F) -> %0xxxxxxx
* %00000xxxxxyyyyyy (0x0080 - 0x07FF) -> %110xxxxx %10yyyyyy
* %xxxxyyyyyyzzzzzz (0x0800 - 0xFFFF) -> %1110xxxx %10yyyyyy %10zzzzzz
*
* Therefore, the inverse is as follows:
* First char:
* 0x00 - 0x7F = one byte char
* 0x80 - 0xBF = invalid
* 0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid)
* 0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid)
* 0xF0 - 0xFF = invalid
*/
/* translates a UTF string into an ISO latin1 string, returns success */
int CAB_convertUTF(unsigned char *in) {
unsigned char c, *out = in, *end = in + strlen(in) + 1;
unsigned int x;
do {
/* read unicode character */
if ((c = *in++) < 0x80) x = c;
else {
if (c < 0xC0) return 0;
else if (c < 0xE0) {
x = (c & 0x1F) << 6;
if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F);
}
else if (c < 0xF0) {
x = (c & 0xF) << 12;
if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F)<<6;
if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F);
}
else return 0;
}
/* terrible unicode -> ISO latin1 conversion */
if (x > 255) x = '_';
if (in > end) return 0; /* just in case */
} while (*out++ = (unsigned char) x);
return 1;
}
static const STRPTR CAB_typenames[] = {
"stored", "MSZIP", "Quantum", "LZX"
};
ASM(BOOL) CAB_RecogData(REG(d0, ULONG size), REG(a0, STRPTR d), XADBASE) {
return (BOOL) ((d[0]=='M' && d[1]=='S' && d[2]=='C' && d[3]=='F') ? 1 : 0);
}
ASM(LONG) SAVEDS CAB_GetInfo(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
UBYTE buf[cfhead_SIZEOF]; /* buffer for reading in structures */
UBYTE namebuf[CAB_NAMEMAX]; /* buffer for file paths */
UBYTE *namep, c; /* for char loops on namebuf */
ULONG base_offset; /* the file offset of the start of this cabinet */
ULONG files_offset; /* the file offset of the first CFFILE in this cabinet */
ULONG end_offset; /* the file offset of the end of this cabinet */
UWORD num_folders; /* the number of CFFOLDERs in this cabinet */
UWORD num_files; /* the number of CFFILEs in this cabinet */
UWORD header_res; /* the empty space reserved in the CFHEADER */
UBYTE folder_res; /* the empty space reserved in each CFFOLDER */
UBYTE data_res; /* the empty space reserved in CFDATA */
int i, x, curfile=1, curvol=-1, mergeok;
struct CABfolder *firstfol; /* first folder in this cabinet */
struct CABfolder *lastfol = NULL; /* last folder in this cabinet */
struct CABfolder *predfol; /* last folder in previous cabinet */
struct CABfolder *linkfol = NULL, *fol; /* folder addition loop */
struct xadFileInfo *link = NULL, *fi; /* file addition loop */
LONG err = XADERR_OK;
struct TagItem filetags[] = {
{ XAD_OBJNAMESIZE, 0 },
{ TAG_DONE, 0 }
};
struct TagItem datetags[] = {
{ XAD_DATEMSDOS, 0 },
{ XAD_GETDATEXADDATE, 0 },
{ TAG_DONE, 0 }
};
struct TagItem prottags[] = {
{ XAD_PROTMSDOS, 0 },
{ XAD_GETPROTAMIGA, 0 },
{ TAG_DONE, 0 }
};
/* attach state information to archive */
ALLOC(APTR, ai->xai_PrivateClient, sizeof(struct CABstate));
while (1) {
/* the below if statement is the natural exit point of this loop */
if (ai->xai_MultiVolume) {
ULONG pos = ai->xai_InPos, next = ai->xai_MultiVolume[++curvol];
D(("CAB: top wanted=%ld actual=%ld\n",next,pos))
/* files end when the 'next file' offset is 0 - except, of course
* for the very first iteration of this loop, because the first
* file's offset is also 0
*/
if (!next && curvol) goto exit_handler; /* end of files */
if (pos < next) SKIP(next - pos); /* skip to next file */
if (pos > next) ERROR(ILLEGALDATA); /* overrun error */
}
else {
/* singlefile exit point - we shouldn't do more than one loop. */
if (++curvol) goto exit_handler;
}
base_offset = ai->xai_InPos;
/* ------------- PROCESS CFHEADER -------------- */
READ(&buf, cfhead_SIZEOF);
files_offset = GETLONG(cfhead_FileOffset) + base_offset;
end_offset = GETLONG(cfhead_CabinetSize) + base_offset;
if ((buf[0]!='M' || buf[1]!='S' || buf[2]!='C' || buf[3]!='F')
|| ((num_folders = GETWORD(cfhead_NumFolders)) == 0)
|| ((num_files = GETWORD(cfhead_NumFiles)) == 0))
ERROR(ILLEGALDATA);
if (GETBYTE(cfhead_MajorVersion) > 1
|| GETBYTE(cfhead_MinorVersion) > 3) ERROR(DATAFORMAT);
/* read 'reserve' part of header if present and skip reserved header */
if ((x = GETWORD(cfhead_Flags)) & cfheadRESERVE_PRESENT) {
READ(&buf, cfheadext_SIZEOF);
header_res = GETWORD(cfheadext_HeaderReserved);
folder_res = GETBYTE(cfheadext_FolderReserved);
data_res = GETBYTE(cfheadext_DataReserved);
SKIP(header_res);
if (header_res > 60000) TAINT("header reserved > 60000");
}
else {
folder_res = data_res = 0;
}
/* skip next and previous cabinet filenames/disknames if present */
i = ((x & cfheadPREV_CABINET) ? 2:0) + ((x & cfheadNEXT_CABINET) ? 2:0);
while (i--) {
int len = 0;
do { READ(&buf, 1); len++; } while (*buf);
if (len > 256) TAINT("nameskip > 256");
}
/* ------------- PROCESS CFFOLDERs ------------- */
firstfol = NULL;
predfol = lastfol;
for (i = 0; i < num_folders; i++) {
READ(&buf, cffold_SIZEOF);
D(("CAB: folder offset=%ld comptype=0x%lx\n",
GETLONG(cffold_DataOffset)+base_offset, GETWORD(cffold_CompType)
))
SKIP(folder_res);
ALLOC(struct CABfolder *, fol, sizeof(struct CABfolder));
fol->offsets[0] = GETLONG(cffold_DataOffset) + base_offset;
fol->comp_type = GETWORD(cffold_CompType);
fol->data_res[0] = data_res;
fol->num_splits = 0;
if (!firstfol) firstfol = fol;
lastfol = fol;
/* link folder into folders list */
if (linkfol) linkfol->next=fol;
else ((struct CABstate *) ai->xai_PrivateClient)->folders = fol;
linkfol = fol;
}
/* firstfol = first folder in this cabinet */
/* lastfol = last folder in this cabinet */
/* predfol = last folder in previous cabinet (or NULL if first cabinet) */
/* assume that this cabinet's split files are OK to merge */
mergeok = 1;
/* ------------- PROCESS CFFILEs ------------- */
if (ai->xai_InPos != files_offset) TAINT("not at file offset");
for (i = 0; i < num_files; i++) {
READ(&buf, cffile_SIZEOF);
/* read filename */
namep = namebuf;
do {
if ((namep - namebuf) > CAB_NAMEMAX) ERROR(NOMEMORY);
READ(namep, 1);
} while (*namep++);
/* convert filename */
if ((x = GETWORD(cffile_Attribs) & cffileUTFNAME)
&& !CAB_convertUTF(namebuf)) TAINT("bad UTF name");
for (namep = namebuf; c = *namep; namep++) {
if (c == '/') *namep = '\\';
if (c == '\\') *namep = '/';
if (!x && c > 127) *namep = '_';
}
D(("CAB: file size=%ld offset=%ld index=0x%lx name=«%s»\n",
GETLONG(cffile_UncompressedSize), GETLONG(cffile_FolderOffset),
GETWORD(cffile_FolderIndex), namebuf
))
/* file information entry */
filetags[0].ti_Data = (x = strlen((char *) namebuf) + 1);
ALLOCOBJ(struct xadFileInfo *, fi, XADOBJ_FILEINFO, filetags);
fi->xfi_EntryNumber = curfile++;
fi->xfi_Size = GETLONG(cffile_UncompressedSize);
fi->xfi_DataPos = GETLONG(cffile_FolderOffset);
xadCopyMem(namebuf, fi->xfi_FileName, x);
prottags[0].ti_Data = GETWORD(cffile_Attribs);
prottags[1].ti_Data = (ULONG) &fi->xfi_Protection;
xadConvertProtectionA(prottags);
datetags[0].ti_Data = (GETWORD(cffile_Date)<<16)|GETWORD(cffile_Time);
datetags[1].ti_Data = (ULONG) &fi->xfi_Date;
xadConvertDatesA(datetags);
/* which folder is this file in? */
x = GETWORD(cffile_FolderIndex);
if (x < num_folders) {
for (fol = firstfol; x--; fol=fol->next);
fi->xfi_PrivateInfo = (APTR) fol;
}
else {
if (ai->xai_MultiVolume) {
/* FOLDER MERGING */
if (x == cffileCONTINUED_TO_NEXT
|| x == cffileCONTINUED_PREV_AND_NEXT) {
D(("CAB: file merge next\n"))
/* this file is in the next cabinet, so we don't set its folder
* as it will be repeated with the 'prev' folder in the next
* cabinet. also, if this file is continued prev and next, it
* can only be a single file extending a single folder beyond
* the cabinet size limits, the next file _has_ to start in a
* new folder. we can test that.
*/
if (x == cffileCONTINUED_PREV_AND_NEXT) {
if (num_folders != 1 || num_files != 1) TAINT("prev/next");
}
if (!lastfol->contfile) lastfol->contfile = fi;
}
if (x == cffileCONTINUED_FROM_PREV
|| x == cffileCONTINUED_PREV_AND_NEXT) {
D(("CAB: file merge prev\n"))
/* if these files are to be continued in yet _another_ cabinet,
* don't merge them in just yet
*/
if (x == cffileCONTINUED_PREV_AND_NEXT) mergeok = 0;
/* only merge once per cabinet */
if (predfol) {
struct xadFileInfo *cfi;
/* in this case, the file states that folder 0 of this cabinet
* is actually part of the last folder in the previous cabinet.
* if this is true, the first 'continued' file of the last
* folder will match the first file of this folder. Also,
* both folders will have the same compression type.
*/
if ((cfi = predfol->contfile)
&& (cfi->xfi_DataPos == fi->xfi_DataPos)
&& (cfi->xfi_Size == fi->xfi_Size)
&& (strcmp(cfi->xfi_FileName, fi->xfi_FileName) == 0)
&& (predfol->comp_type == firstfol->comp_type)) {
/* free the fileinfo kept for testing if last occurance */
if (x == cffileCONTINUED_FROM_PREV) {
FREE(predfol->contfile);
predfol->contfile = NULL;
}
/* increase the number of splits */
if ((x = ++(predfol->num_splits)) > CAB_SPLITMAX)
ERROR(DATAFORMAT);
/* copy information across from the merged folder */
predfol->offsets[x] = firstfol->offsets[0];
predfol->data_res[x] = firstfol->data_res[0];
predfol->next = firstfol->next;
predfol->contfile = firstfol->contfile;
if (firstfol == lastfol) lastfol = linkfol = predfol;
FREE(firstfol);
firstfol = predfol;
predfol = NULL; /* don't merge again within this cabinet */
}
else {
/* if the merged folders are incompatible, certainly
* don't list the files in them
*/
mergeok = 0;
}
}
/* only add split files at their final appearance
* and only if merging was actually possible
*/
if (mergeok) fi->xfi_PrivateInfo = (APTR) firstfol;
}
}
else {
/* not multivolume - can't do a folder merge, therefore it either
* IS a merge but there's no next/prev cabinet (missing data),
* or it's an out of range folder index (corrupt data)
*/
TAINT("folder index");
}
}
/* if there's no folder, skip this file */
if (!fi->xfi_PrivateInfo) {
/* but only free it if never used again */
if (fi != lastfol->contfile) FREE(fi);
continue;
}
else {
/* use compression mode of this file's folder as entryinfo name */
UWORD ct = CABFILEFOL(fi)->comp_type & cffoldCOMPTYPE_MASK;
if (ct <= cffoldCOMPTYPE_LZX) fi->xfi_EntryInfo = CAB_typenames[ct];
}
/* link file into file list */
if (link) link->xfi_Next = fi; else ai->xai_FileInfo = fi;
link = fi;
}
/* Skip looking at the CFDATA blocks. Why? Well, it requires us to read
* and skip the entire file, and the only information we get from doing
* that is how large each folder is. Why not just use the offsets
* between folders to define that? It's slightly less accurate - some
* would say more accurate because it counts the block header overhead
* - and there will be a calculation error if the folders are not
* defined in order of data block appearance [I haven't seen any such
* files], but overall it's much faster
*/
for (fol = firstfol; fol; fol=fol->next) {
fol->comp_size =
((fol->next) ? fol->next->offsets[0] : end_offset) - fol->offsets[0];
}
}
exit_handler:
/* free any continuation comparison files in use */
fol = ((struct CABstate *) ai->xai_PrivateClient)->folders;
for (; fol; fol=fol->next) {
if (fol->contfile) {
TAINT("incomplete folder merge");
FREE(fol->contfile);
}
}
/* fill in group crunched sizes */
for (fi = ai->xai_FileInfo; fi; fi = fi->xfi_Next) {
fi->xfi_Flags = XADFIF_GROUPED;
if (!fi->xfi_Next || (CABFILEFOL(fi) != CABFILEFOL(fi->xfi_Next))) {
fi->xfi_Flags |= XADFIF_ENDOFGROUP;
fi->xfi_GroupCrSize = CABFILEFOL(fi)->comp_size;
}
}
/* if tainted, add the tainted lasterror */
if (ai->xai_Flags & XADAIF_FILECORRUPT)
ai->xai_LastError = XADERR_ILLEGALDATA;
/* if a real error, then if we have files, taint and set, otherwise quit */
if (err) {
if (!ai->xai_FileInfo) return err;
ai->xai_Flags |= XADAIF_FILECORRUPT;
ai->xai_LastError = err;
D(("CAB: info error=%ld\n", err))
}
return XADERR_OK;
}
/*--------------------------------------------------------------------------*/
/* UnArchive / Free section */
LONG CAB_NONEdecompress(CABSTATE, int inlen, int outlen) {
struct xadMasterBase *xadMasterBase = CAB(xad);
if (inlen != outlen) return XADERR_ILLEGALDATA;
xadCopyMem(CAB(inbuf), CAB(outbuf), inlen);
return XADERR_OK;
}
ULONG CAB_checksum(UBYTE *data, UWORD bytes, ULONG csum) {
int len;
ULONG ul = 0;
for (len = bytes >> 2; len--; data += 4) {
csum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
}
switch (bytes & 3) {
case 3: ul |= *data++ << 16;
case 2: ul |= *data++ << 8;
case 1: ul |= *data;
}
csum ^= ul;
return csum;
}
LONG CAB_decompress(CABSTATE, ULONG bytes, int save) {
struct xadArchiveInfo *ai = CAB(ai);
struct xadMasterBase *xadMasterBase = CAB(xad);
UBYTE buf[cfdata_SIZEOF], *data;
UWORD inlen, len, outlen, cando;
ULONG cksum;
LONG err = XADERR_OK;
/* here's an optimisation to prevent the re-decoding of a folder with an
* error at some position in it. If a file or skip goes beyond the point
* where we last got an error, then we won't bother going through it all
* again, we'll just repeat the error.
*/
if (CAB(current) == CAB(lastfolder)
&& (CAB(offset) + bytes) > CAB(lastoffset))
return CAB(lasterror);
while (bytes > 0) {
/* cando = the max number of bytes we can do */
cando = CAB(outlen);
if (cando > bytes) cando = bytes;
D(("CAB: decomp bytes=%ld cando=%ld\n", bytes, cando))
if (cando && save) WRITE(CAB(outpos), cando); /* if cando != 0 */
CAB(outpos) += cando;
CAB(outlen) -= cando;
bytes -= cando; if (!bytes) break;
/* we only get here if we emptied the output buffer */
/* read data header + data */
inlen = outlen = 0;
while (outlen == 0) {
/* read the block header, skip the reserved part */
READ(buf, cfdata_SIZEOF);
SKIP(CAB(current)->data_res[CAB(split)]);
/* we shouldn't get blocks over CAB_INPUTMAX in size */
data = CAB(inbuf) + inlen;
if ((inlen += (len = GETWORD(cfdata_CompressedSize))) > CAB_INPUTMAX)
ERROR(INPUT);
READ(data, len);
/* perform checksum test on the block (if one is stored) */
if ((cksum = GETLONG(cfdata_CheckSum)) != 0) {
if (cksum != CAB_checksum(buf+4, 4, CAB_checksum(data, len, 0)))
ERROR(CHECKSUM);
}
/* outlen=0 means this block was part of a split block */
if ((outlen = GETWORD(cfdata_UncompressedSize)) == 0) {
CAB(split)++; SEEK(CAB(current)->offsets[CAB(split)]);
}
}
/* decompress block */
if ((err = CAB(decompress)(cabstate, inlen, outlen))) goto exit_handler;
CAB(outlen) = outlen;
CAB(outpos) = CAB(outbuf);
}
exit_handler:
if (err) {
if (err == XADERR_INPUT
|| err == XADERR_ILLEGALDATA
|| err == XADERR_DECRUNCH
|| err == XADERR_CHECKSUM
|| err == XADERR_DATAFORMAT) {
CAB(lasterror) = err;
CAB(lastfolder) = CAB(current);
CAB(lastoffset) = CAB(offset);
}
/* reset folder */
CAB(current) = NULL;
}
return err;
}
ASM(LONG) SAVEDS CAB_UnArchive(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
struct CABstate *cabstate = (struct CABstate *) ai->xai_PrivateClient;
struct xadFileInfo *file = ai->xai_CurFile;
struct CABfolder *fol = CABFILEFOL(file);
LONG err = XADERR_OK;
CAB(ai) = ai;
CAB(xad) = xadMasterBase;
/* is a change of folder needed? do we need to reset the current folder? */
if (fol != CAB(current) || file->xfi_DataPos < CAB(offset)) {
UWORD comptype = fol->comp_type;
/* if the archiver has changed, call the old archiver's free() function */
if (CAB(free) && CAB(current) && ((comptype & cffoldCOMPTYPE_MASK)
!= (CAB(current)->comp_type & cffoldCOMPTYPE_MASK))) CAB(free)(cabstate);
switch (comptype & cffoldCOMPTYPE_MASK) {
case cffoldCOMPTYPE_NONE:
CAB(decompress) = CAB_NONEdecompress;
CAB(free) = NULL;
break;
case cffoldCOMPTYPE_MSZIP:
CAB(decompress) = CAB_ZIPdecompress;
CAB(free) = NULL;
break;
case cffoldCOMPTYPE_QUANTUM:
CAB(decompress) = CAB_QTMdecompress;
CAB(free) = CAB_QTMfree;
err = CAB_QTMinit(cabstate, (comptype>>4) & 0xf, (comptype>>8) & 0x1f);
break;
case cffoldCOMPTYPE_LZX:
CAB(decompress) = CAB_LZXdecompress;
CAB(free) = CAB_LZXfree;
err = CAB_LZXinit(cabstate, (comptype >> 8 & 0x1f));
break;
default:
err = XADERR_NOTSUPPORTED;
}
if (err) goto exit_handler;
/* initialisation OK, set current folder and reset offset */
SEEK(fol->offsets[0]);
CAB(current) = fol;
CAB(offset) = 0;
CAB(outlen) = 0; /* discard existing block */
CAB(split) = 0;
}
if (file->xfi_DataPos > CAB(offset)) {
D(("CAB: unarc skipping %ld bytes\n",file->xfi_DataPos-CAB(offset)))
/* decode bytes and send them to /dev/null */
if ((err = CAB_decompress(cabstate, file->xfi_DataPos-CAB(offset), 0)))
return err;
else
CAB(offset) = file->xfi_DataPos;
}
/* decode bytes and save them */
D(("CAB: unarc decoding %ld bytes\n",file->xfi_Size))
if (!(err = CAB_decompress(cabstate, file->xfi_Size, 1)))
CAB(offset) += file->xfi_Size;
exit_handler:
return err;
}
ASM(void) CAB_Free(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
struct CABstate *cabstate = (struct CABstate *) ai->xai_PrivateClient;
struct CABfolder *f, *nf;
if (!cabstate) return;
if (CAB(free)) CAB(free)(cabstate); /* call archiver's free() function */
for (f = CAB(folders); f; f = nf) { nf = f->next; FREE(f); }
FREE(cabstate);
ai->xai_PrivateClient = NULL;
}
/*--------------------------------------------------------------------------*/
/* executable loader */
ASM(BOOL) CABEXE_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *d), XADBASE) {
if (size < 20000 || d[0] != 'M' || d[1] != 'Z') return 0;
/* word aligned code signature: 817C2404 "MSCF" (found at random, sorry) */
for (d+=8, size-=8; size >= 12; d+=2, size-=2) {
if (d[0]==0x81 && d[1]==0x7C && d[2]==0x24 && d[3]==0x04
&& d[4]=='M' && d[5]=='S' && d[6]=='C' && d[7]=='F')
return 1;
/* another revision: 817DDC "MSCF" */
if (d[0]==0x81 && d[1]==0x7D && d[2]==0xDC
&& d[3]=='M' && d[4]=='S' && d[5]=='C' && d[6]=='F')
return 1;
}
return 0;
}
#define CABEXE_START (55 * 1024) /* search from 55k (StartBTClick.exe) */
#define CABEXE_END (150 * 1024) /* search to 150k (says dirk) */
ASM(LONG) CABEXE_GetInfo(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
/* offset=0 as sentinel is OK; file has to start "MZ" to get here */
ULONG filelen, readlen, offset = 0, i;
UBYTE *buf, *p;
LONG err;
filelen = ai->xai_InSize;
if ((readlen = filelen - CABEXE_START) > (CABEXE_END - CABEXE_START)) {
readlen = CABEXE_END - CABEXE_START;
}
ALLOC(UBYTE *, buf, readlen);
SKIP(CABEXE_START);
READ(buf, readlen);
readlen -= cfhead_SIZEOF;
for (i=0, p=buf; i < readlen; i++, p++) {
/* if we find a header */
if (p[0]=='M' && p[1]=='S' && p[2]=='C' && p[3]=='F') {
/* read the 'length of cab file and 'offset within cab file' */
ULONG len = (p[8]) | (p[9]<<8) | (p[10]<<16) | (p[11]<<24);
ULONG foff = (p[16]) | (p[17]<<8) | (p[18]<<16) | (p[19]<<24);
/* if these lengths are consistent, we have a match! */
if (len < filelen && foff < len) {
offset = CABEXE_START + i;
D(("CAB: exe offset=%ld\n",offset))
goto exit_handler;
}
}
}
err = XADERR_DATAFORMAT;
exit_handler:
if (buf) FREE(buf);
/* if we found a match, actually look at the archive */
if (offset != 0) {
SEEK(offset);
err = CAB_GetInfo(ai, xadMasterBase);
}
return err;
}
/*--------------------------------------------------------------------------*/
const struct xadClient CABEXE_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 6, CAB_VERSION, CAB_REVISION,
24576, /* first 24kb should have signature */
XADCF_FILEARCHIVER|XADCF_FREEFILEINFO|XADCF_FREESKIPINFO|XADCF_NOCHECKSIZE,
0, "CAB MS-EXE",
/* client functions */
(BOOL (*)()) CABEXE_RecogData,
(LONG (*)()) CABEXE_GetInfo,
(LONG (*)()) CAB_UnArchive,
(void (*)()) CAB_Free
};
const struct xadClient CAB_Client = {
(struct xadClient *) &CABEXE_Client,
XADCLIENT_VERSION, 6, CAB_VERSION, CAB_REVISION,
14,
XADCF_FILEARCHIVER | XADCF_FREEFILEINFO | XADCF_FREESKIPINFO,
0, "CAB",
/* client functions */
(BOOL (*)()) CAB_RecogData,
(LONG (*)()) CAB_GetInfo,
(LONG (*)()) CAB_UnArchive,
(void (*)()) CAB_Free
};